﻿using System;
using System.Collections.Generic;
using Unity.Entities;
using UnityEngine;

namespace Jinndev.GameKit {

    /// <summary>
    /// 库存封装类，包含一个Item[]存储库存数据，支持触发库存改变事件
    /// </summary>
    public class Inventory {

        /// <summary>
        /// 库存数据
        /// </summary>
        public Item[] Items { get; private set; }

        /// <summary>
        /// 库存设置
        /// </summary>
        public InventorySetting Setting { get; set; }

        /// <summary>
        /// 库存改变回调<索引数组>，可为空
        /// </summary>
        public Action<int[]> onChanged;
        /// <summary>
        /// 被快速转移进物品
        /// </summary>
        public Action onQuickTransferred;

        /// <summary>
        /// 快速转移的目标库存，可为空
        /// </summary>
        public Inventory quickTransferTarget;

        /// <summary>
        /// 最近一次库存操作导致发生变化的索引列表，-1表示设置了整体库存
        /// </summary>
        public List<int> changedIndexList = new List<int>();

        /// <summary>
        /// 初始化库存类
        /// </summary>
        /// <param name="setting">库存设置</param>
        /// <param name="items">库存数据数组，可延迟设置</param>
        public Inventory(InventorySetting setting, Item[] items = null) {
            Setting = setting;
            Items = items;
        }

        public Item this[int index] {
            get {
                return Items == null ? Item.Empty : Items[index];
            }
        }

        public int Count { get { return Items == null ? 0 : Items.Length; } }

        /// <summary>
        /// 设置称新的库存物品数组
        /// </summary>
        public void SetItems(Item[] items) {
            changedIndexList.Clear();
            changedIndexList.Add(-1);
            Items = items;
            InvokeOnChanged(changedIndexList.ToArray());
        }

        public void SetItems(DynamicBuffer<Item> buffer) {
            Item[] items = new Item[buffer.Length];
            for (int i = 0; i < buffer.Length; i++) {
                items[i] = buffer[i];
            }
            SetItems(items);
        }

        public bool IsSame(int index, int id) {
            return Items != null && Items[index].id == id;
        }

        public bool HasItem(int index) {
            return Items != null && Items[index].HasItem();
        }

        /// <summary>
        /// 在指定格子放入或拿取物品，取决于num的正负
        /// </summary>
        /// <returns>返回没放完剩下的数量</returns>
        public int AddTo(int index, int id, int num, int max) {
            changedIndexList.Clear();
            if (Items == null) {
                return num;
            }
            int leftNum = num;

            if (Items[index].IsEmptyID()) {
                // 空白
                if (leftNum > 0) {
                    // num>0，说明是放入物品，直接放入
                    Items[index] = new Item(id, 0, max);
                    leftNum = Items[index].Add(leftNum);
                }
            }
            else {
                if (Items[index].id == id) {
                    // 相同物品，尝试合并/拿取
                    leftNum = Items[index].Add(leftNum);
                }
                else {
                    // 其他物品
                    if (Items[index].num == 0) {
                        // 空白，当SetEmptyOnZero=false时会出现此情况
                        // 可以覆盖，无法拿取
                        if (leftNum > 0) {
                            // 覆盖
                            Items[index] = new Item(id, 0, max);
                            leftNum = Items[index].Add(leftNum);
                        }
                    }
                    else {
                        // 非空白
                        // 无法放入和拿取
                    }
                }
            }

            if (leftNum != num) {
                changedIndexList.Add(index);
                // 确实发生了改变
                InvokeOnChanged(changedIndexList.ToArray());
            }
            // 返回剩下的数量
            return leftNum;
        }

        /// <summary>
        /// 交换指定物品槽，并获得原槽位的物品
        /// </summary>
        /// <param name="index"></param>
        /// <param name="id"></param>
        /// <param name="num"></param>
        /// <param name="originItem"></param>
        /// <returns></returns>
        public bool Replace(int index, Item item, out Item originItem) {
            changedIndexList.Clear();
            originItem = Item.Empty;
            if (Items == null) {
                return false;
            }

            originItem = Items[index];
            Items[index] = item;
            if (originItem.id != item.id || originItem.num != item.num) {
                changedIndexList.Add(index);
                InvokeOnChanged(changedIndexList.ToArray());
                return true;
            }
            return false;
        }

        /// <summary>
        /// 无视上限直接设置指定格子物品
        /// </summary>
        public void SetNum(int index, int num) {
            changedIndexList.Clear();
            if (Items == null) {
                return;
            }
            bool changed = Items[index].num != num;
            Items[index].SetNum(num, Setting.setEmptyOnZero);

            if (changed) {
                changedIndexList.Add(index);
                InvokeOnChanged(changedIndexList.ToArray());
            }
        }

        /// <summary>
        /// 不适用setEmptyOnZero
        /// </summary>
        /// <param name="index"></param>
        /// <param name="item"></param>
        public void SetItem(int index, Item item) {
            changedIndexList.Clear();
            if (Items == null) {
                return;
            }
            bool changed = !Items[index].Equals(item);
            Items[index] = item;

            if (changed) {
                changedIndexList.Add(index);
                InvokeOnChanged(new int[] { index });
            }
        }

        /// <summary>
        /// 放入物品
        /// </summary>
        /// <returns>返回没放完剩下的数量</returns>
        public int Add(int id, int num, int max) {
            changedIndexList.Clear();
            if (Items == null) {
                return num;
            }
            int leftNum = num;
            if (leftNum != 0) {
                // 第一轮，尝试找到相同的物品进行合并/拿取
                for (int i = 0; i < Items.Length; i++) {
                    if (Items[i].id == id) {
                        leftNum = Items[i].Add(leftNum);
                        changedIndexList.Add(i);
                        if (leftNum == 0) {
                            break;
                        }
                    }
                }
            }
            if (leftNum > 0) {
                // 第二轮，尝试找到空格子放入
                for (int i = 0; i < Items.Length; i++) {
                    if (Items[i].IsEmptyID()) {
                        Items[i] = new Item(id, 0, max);
                        leftNum = Items[i].Add(leftNum);
                        changedIndexList.Add(i);
                        if (leftNum == 0) {
                            break;
                        }
                    }
                }
            }
            if (leftNum != num) {
                // 确实发生了改变
                InvokeOnChanged(changedIndexList.ToArray());
            }
            // 返回剩下的数量
            return leftNum;
        }

        public void InvokeOnChanged(int[] changedIndexArray) {
            if (!string.IsNullOrEmpty(Setting.onChangedKey)) {
                EventManager.Invoke(Setting.onChangedKey);
            }
            onChanged?.Invoke(changedIndexArray);
        }

    }

    /// <summary>
    /// 库存设置
    /// </summary>
    public struct InventorySetting {
        /// <summary>
        /// 库存改变时通知EventManager的key
        /// </summary>
        public string onChangedKey;
        /// <summary>
        /// 数量为0时是否设置为EmptyID
        /// </summary>
        public bool setEmptyOnZero;
        /// <summary>
        /// 拿起物品时的行为
        /// </summary>
        public PickBehaviour pickBehaviour;
        /// <summary>
        /// 放入自身库存时的行为
        /// </summary>
        public DropIntoSelfInventory dropIntoSelfInventory;
        /// <summary>
        /// 放入其他库存时的行为
        /// </summary>
        public DropIntoOtherInventory dropIntoOtherInventory;
        /// <summary>
        /// 接收其他库存物品时的行为
        /// </summary>
        public ReceiveFromOtherInventory receiveFromOtherInventory;
        /// <summary>
        /// 放入背景界面时的行为
        /// </summary>
        public DropOnBackground dropOnBackground;


        public InventorySetting(string onChangedKey, bool setEmptyOnZero = false) {
            this.onChangedKey = onChangedKey;
            this.setEmptyOnZero = setEmptyOnZero;

            pickBehaviour = PickBehaviour.Disable;
            dropIntoSelfInventory = DropIntoSelfInventory.Disable;
            dropIntoOtherInventory = DropIntoOtherInventory.Cancel;
            receiveFromOtherInventory = ReceiveFromOtherInventory.Disable;
            dropOnBackground = DropOnBackground.PutBack;
        }

    }

}
